﻿using System;
using System.Runtime.InteropServices;

class _VideoStream
{

    /// <summary>
    /// 视频像素宽
    /// </summary>
    public int pixelwidth;

    /// <summary>
    /// 视频像素高
    /// </summary>
    public int pixelheight;

    /// <summary>
    /// 视频fps
    /// </summary>
    public double fps;

    /// <summary>
    /// 视频像素格式
    /// AV_PIX_FMT_YUV420P = 0
    /// AV_PIX_FMT_YUVJ420P = 12
    /// </summary>
    public int pixelfmt;
}
class _AudioStream
{
    /// <summary>
    /// 音频流数量
    /// </summary>
    public int streamCount;

    /// <summary>
    /// 音频采样率
    /// </summary>
    public int samplerate;

    /// <summary>
    /// 音频采样率
    /// </summary>
    public int samplesize;

    /// <summary>
    /// 音频通道数
    /// </summary>
    public int channels;

    public int channel_layout;

    /// <summary>
    /// 音频格式
    /// </summary>
    public int samplefmt;
}
class _DemuxInfo
{

    public int retcode;

    /*
     * 总时长 单位：毫秒
     */
    public long duration;

    public _VideoStream videoStream;

    public _AudioStream srcAudioStream;

    public _AudioStream desAudioStream;
}

public struct _Frame
{
    public bool isVideoFrame;
    public int width;
    public int height;
    public IntPtr[] data;
    public int[] linesize;
    public long pts;
    public int format;
};

public enum VDOptionType
{
    OT_OpenTimeout = 1,
    OT_ReadPacketTimeout,
    OT_EnableVideo,
    OT_EnableAudio,
    OT_Loop,
    OT_OutputPixFmt,
    OT_OpenModel,
    OT_NoBuffer,
    OT_AutoPlay
}
class VDModule
{
    public const string moduleName = "VDModule.dll";

    public const int OPEN_MODEL_VIDEO = 0;
    public const int OPEN_MODEL_CAMERA = 1;
    public const int OPEN_MODEL_MICROPHONE = 2;

    public const int OUTPUT_PIXFMT_DEFAULT = -1;
    public const int OUTPUT_PIXFMT_RGB24 = 0;
    public const int OUTPUT_PIXFMT_BGR24 = 1;
    public const int OUTPUT_PIXFMT_ARGB = 2;
    public const int OUTPUT_PIXFMT_RGBA = 3;
    public const int OUTPUT_PIXFMT_ABGR = 4;
    public const int OUTPUT_PIXFMT_BGRA = 5;

    private static VDModuleLogPtr logptr = VDModuleLog;
    public static DemuxCbPtr demuxCallBack = DemuxCallback;
    public static FrameCbPtr frameCallBack = FrameCallback;

    [StructLayout(LayoutKind.Sequential)]
    private struct VideoStream
    {
        /*
         * 视频像素宽
         */
        public int pixelwidth;
        /*
         * 视频像素高
         */
        public int pixelheight;
        /*
         * 视频fps
         */
        public double fps;
        /*
         * 视频像素格式
         * AV_PIX_FMT_YUV420P = 0
         * AV_PIX_FMT_YUVJ420P = 12
         */
        public int pixelfmt;
    };

    [StructLayout(LayoutKind.Sequential)]
    private struct AudioStream
    {

        public int streamCount;

        /*
         * 音频采样率
         */
        public int samplerate;

        /*
         * 音频采样率
         */
        public int samplesize;

        /*
         * 音频通道数
         */
        public int channels;

        public int channel_layout;

        /*
         * 音频格式
         */
        public int samplefmt;
    };

    [StructLayout(LayoutKind.Sequential)]
    private struct DemuxInfo
    {
        /*
         * 0:打开文件成功
         * -1:ffmpeg 输出
         * -2:demux已经存在
         * -3:avformat_alloc_context返回NULL
         * -4:程序退出
         * -5:等待超时
         */
        public int retcode;

        /*
         * 总时长 单位：毫秒
         */
        public long duration;

        public IntPtr videoStream;

        public IntPtr srcAudioStream;

        public IntPtr desAudioStream;
    };


    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    private delegate void VDModuleLogPtr(int level, IntPtr log);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void DemuxCbPtr(IntPtr owner, IntPtr info);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void CloseCbPtr(IntPtr owner, int state);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void FrameCbPtr(IntPtr owner, IntPtr frame);


    [DllImport(moduleName, CallingConvention = CallingConvention.Cdecl)]
    private extern static int VDModuleSetLog(VDModuleLogPtr funcPtr);

    [DllImport(moduleName, CallingConvention = CallingConvention.Cdecl)]
    public extern static IntPtr CreateVDPlayer(IntPtr owner);

    [DllImport(moduleName, CallingConvention = CallingConvention.Cdecl)]
    public extern static void DeleteVDPlayer(IntPtr vdplayer);

    [DllImport(moduleName, CallingConvention = CallingConvention.Cdecl)]
    public extern static void Open(IntPtr vdplayer, byte[] filename, DemuxCbPtr callback);


    [DllImport(moduleName, CallingConvention = CallingConvention.Cdecl)]
    public extern static void Close(IntPtr vdplayer);

    [DllImport(moduleName, CallingConvention = CallingConvention.Cdecl)]
    public extern static void SetOption(IntPtr vdplayer, int optionType, int value);

    [DllImport(moduleName, CallingConvention = CallingConvention.Cdecl)]
    public extern static void Play(IntPtr vdplayer);

    [DllImport(moduleName, CallingConvention = CallingConvention.Cdecl)]
    public extern static void Pause(IntPtr vdplayer);

    [DllImport(moduleName, CallingConvention = CallingConvention.Cdecl)]
    public extern static int StartDemux(IntPtr vdplayer, bool playVideo, bool tryPlayAudio, FrameCbPtr callback);

    [DllImport(moduleName, CallingConvention = CallingConvention.Cdecl)]
    public extern static void Seek(IntPtr vdplayer, double seekpos);

    [DllImport(moduleName, CallingConvention = CallingConvention.Cdecl)]
    public extern static int GetIsPlaying(IntPtr vdplayer);

    [DllImport(moduleName, CallingConvention = CallingConvention.Cdecl)]
    public extern static void SetAudioTrack(IntPtr vdplayer, int index);

    [DllImport(moduleName, CallingConvention = CallingConvention.Cdecl)]
    public extern static double GetProgress(IntPtr vdplayer);

    [DllImport(moduleName, CallingConvention = CallingConvention.Cdecl)]
    public extern static int ScreenShot(IntPtr vdplayer, ref int width, ref int height, ref IntPtr data);

    [DllImport(moduleName, CallingConvention = CallingConvention.Cdecl)]
    public extern static void SetCameraRect(IntPtr vdplayer, int width, int height);


    public static bool SetVDModuleLog()
    {
        return VDModuleSetLog(logptr) == 0 ? true : false;
    }

    /// <summary>
    /// 日志回调函数
    /// </summary>
    /// <param name="level"></param>
    /// <param name="log"></param>
    private static void VDModuleLog(int level, IntPtr log)
    {
       UnityEngine.Debug.Log(Marshal.PtrToStringAnsi(log));
    }

    /// <summary>
    /// 打开成功后的回调函数
    /// </summary>
    /// <param name="owner"></param>
    /// <param name="info"></param>
    private static void DemuxCallback(IntPtr owner, IntPtr info)
    {
        _DemuxInfo infoObj = new _DemuxInfo();

        DemuxInfo di = (DemuxInfo)Marshal.PtrToStructure(info, typeof(DemuxInfo));
        AudioStream desAs = new AudioStream();
        VideoStream vs = new VideoStream();
        if (di.srcAudioStream != IntPtr.Zero)
        {
            AudioStream srcAs = (AudioStream)Marshal.PtrToStructure(di.srcAudioStream, typeof(AudioStream));

            _AudioStream _srcAs = new _AudioStream();
            _srcAs.channels = srcAs.channels;
            _srcAs.channel_layout = srcAs.channel_layout;
            _srcAs.samplefmt = srcAs.samplefmt;
            _srcAs.samplerate = srcAs.samplerate;
            _srcAs.samplesize = srcAs.samplesize;
            _srcAs.streamCount = srcAs.streamCount;

            infoObj.srcAudioStream = _srcAs;
        }

        if (di.desAudioStream != IntPtr.Zero)
        {
            desAs = (AudioStream)Marshal.PtrToStructure(di.desAudioStream, typeof(AudioStream));

            _AudioStream _desAs = new _AudioStream();
            _desAs.channels = desAs.channels;
            _desAs.channel_layout = desAs.channel_layout;
            _desAs.samplefmt = desAs.samplefmt;
            _desAs.samplerate = desAs.samplerate;
            _desAs.samplesize = desAs.samplesize;
            _desAs.streamCount = desAs.streamCount;

            infoObj.desAudioStream = _desAs;
        }

        if (di.videoStream != IntPtr.Zero)
        {
            vs = (VideoStream)Marshal.PtrToStructure(di.videoStream, typeof(VideoStream));

            _VideoStream _vs = new _VideoStream();
            _vs.fps = vs.fps;
            _vs.pixelfmt = vs.pixelfmt;
            _vs.pixelheight = vs.pixelheight;
            _vs.pixelwidth = vs.pixelwidth;

            infoObj.videoStream = _vs;
        }

        infoObj.retcode = di.retcode;
        infoObj.duration = di.duration;

        GCHandle handle = GCHandle.FromIntPtr(owner);
        ((VDPlayer)handle.Target).OpenCallback(infoObj);
    }


    public static long GetTimestamp()
    {
        //获取从1970年一月一日0点0分0秒0微妙开始
        return (DateTime.UtcNow.Ticks - new DateTime(1970, 1, 1, 0, 0, 0, 0).Ticks) / 10000;
    }
    private static void FrameCallback(IntPtr owner, IntPtr frame)
    {
       
        _Frame _frame = new _Frame();
        _frame.isVideoFrame = Marshal.ReadByte(frame) == 0 ? false : true;
        _frame.width = Marshal.ReadInt32(frame, 4);
        _frame.height = Marshal.ReadInt32(frame, 8);

        int index = 0;

        _frame.data = new IntPtr[8];
        for (int offset = 16; offset < 80; offset += 8)
        {
            _frame.data[index] = Marshal.ReadIntPtr(frame, offset);
            index++;
        }
        index = 0;
        _frame.linesize = new int[8];
        for (int offset = 80; offset < 112; offset += 4)
        {
            _frame.linesize[index] = Marshal.ReadInt32(frame, offset);
            index++;
        }

        _frame.pts = Marshal.ReadInt64(frame, 112);
        _frame.format = Marshal.ReadInt32(frame, 120);


        try
        {
            GCHandle handle = GCHandle.FromIntPtr(owner);
            ((VDPlayer)handle.Target).OnFrame(_frame);
        }
        finally { }
       
    }
}